package compiler.my_parser;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Created by szj on 2017/5/30.
 */
public class Production {
    private int prodNum;
    private int dotPosition;
    private int leftPortion;
    private int[] rightPortion;
    private List<Integer> lookAhead = new ArrayList<>();

    public Production(int prodNum, int dotPosition, int leftPortion, int[] rightPortion) {
        this.prodNum = prodNum;
        this.dotPosition = dotPosition;
        this.leftPortion = leftPortion;
        this.rightPortion = rightPortion;
        this.lookAhead.add(SymbolDefinition.EOF.ordinal());
    }

    public void println() {
        boolean printedDot = false;
        System.out.print(SymbolDefinition.getName(this.getLeftPortion()) + " -> ");
        for (int i = 0; i < this.getRightPortion().length; i++) {
            if (this.dotPosition == i) {
                printedDot = true;
                System.out.print(".");
            }
            System.out.print(SymbolDefinition.getName(this.getRightPortion()[i]) + " ");
        }

        if (!printedDot) {
            System.out.print(".");
        }

        System.out.print("  lookahead is { ");
        for (int i : this.lookAhead) {
            System.out.print(SymbolDefinition.getName(i) + " ");
        }
        System.out.println("}");
    }

    public int getDotSymbol() {
        if (dotPosition >= rightPortion.length) {
            return SymbolDefinition.UNKNOWN.ordinal();
        }

        return rightPortion[dotPosition];
    }

    public List<Integer> computeFirstSetOfBetaAndC() {

        List<Integer> set = new ArrayList<>();
        for (int i = dotPosition + 1; i < rightPortion.length; i++) {
            set.add(rightPortion[i]);
        }

        List<Integer> firstSet = new ArrayList<>();

        //beta 部分不为空,计算FIRST(B)作为该表达式的lookahead集合
        if (set.size() > 0) {
            FirstSetBuilder firstSetBuilder = new FirstSetBuilder();
            for (int i = 0; i < set.size(); i++) {

                List<Integer> lookAhead = firstSetBuilder.getFirstSet(set.get(i));

                lookAhead.stream()
                        .filter(aLookAhead -> !firstSet.contains(aLookAhead))
                        .forEach(firstSet::add);

                if (!firstSetBuilder.isSymbolNullable(set.get(i))) {
                    break;
                }

                if (i == set.size() - 1) {
                    //beta is composed by nulleable terms
                    firstSet.addAll(this.lookAhead);
                }
            }
        } else {
            //如果beta 部分为空的话，直接将上一个表达式的lookaHead集合作为该表达式的lookAhead集合
            firstSet.addAll(lookAhead);
        }

        return firstSet;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Production)) {
            return false;
        }
        Production production = (Production) obj;

        return productionEquals(production)
                && lookAheadEquals(production);
    }

    private boolean productionEquals(Production production) {
        return this.getLeftPortion() == production.getLeftPortion()
                && this.getRightPortion() == production.getRightPortion()
                && this.getDotPosition() == production.getDotPosition();
    }

    private boolean lookAheadEquals(Production production) {
        return Objects.equals(this.getLookAhead(), production.getLookAhead());
    }

    public int getProdNum() {
        return prodNum;
    }

    public void setProdNum(int prodNum) {
        this.prodNum = prodNum;
    }

    public int getDotPosition() {
        return dotPosition;
    }

    public void setDotPosition(int dotPosition) {
        this.dotPosition = dotPosition;
    }

    public int getLeftPortion() {
        return leftPortion;
    }

    public void setLeftPortion(int leftPortion) {
        this.leftPortion = leftPortion;
    }

    public int[] getRightPortion() {
        return rightPortion;
    }

    public void setRightPortion(int[] rightPortion) {
        this.rightPortion = rightPortion;
    }

    public List<Integer> getLookAhead() {
        return lookAhead;
    }

    public void setLookAhead(List<Integer> lookAhead) {
        this.lookAhead = lookAhead;
    }
}
